package com.usbserial;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

import androidx.appcompat.app.AppCompatActivity;

import com.itianshan.wheel.BuildConfig;
import com.itianshan.wheel.R;
import com.itianshan.wheel.WheelView;
import com.usbserial.driver.UsbSerialDriver;
import com.usbserial.driver.UsbSerialPort;
import com.usbserial.driver.UsbSerialProber;
import com.usbserial.util.HexDump;
import com.usbserial.util.SerialInputOutputManager;

import java.io.IOException;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.concurrent.Executors;

public class UsbWheelTestActivity extends AppCompatActivity implements SerialInputOutputManager.Listener{
    private int speed;
    private int direction;

    private String mDeviceName;
    private String mDeviceAddress;
    private WheelView wheelView_01,wheelView_02;
    private TextView textView_01,textView_02;
    private boolean mConnected = false;
    int tx_count = 0;
    private TextView mConnectionState;
    private TextView send_count;
    private TextView rx_data_id_1;
    boolean send_hex = true;//HEX格式发送数据  透传
    boolean rx_hex = false;//HEX格式接收数据  透传
    private StringBuffer sbValues;

    private enum UsbPermission { Unknown, Requested, Granted, Denied };

    private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB";
    private static final int WRITE_WAIT_MILLIS = 2000;
    private static final int READ_WAIT_MILLIS = 2000;

    private int deviceId, portNum, baudRate;
    private boolean withIoManager;

    private BroadcastReceiver broadcastReceiver;
    private Handler mainLooper;
    private TextView receiveText;
    private ControlLines controlLines;

    private SerialInputOutputManager usbIoManager;
    private UsbSerialPort usbSerialPort;
    private UsbPermission usbPermission = UsbPermission.Unknown;
    private boolean connected = false;

    public UsbWheelTestActivity() {
        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if(intent.getAction().equals(INTENT_ACTION_GRANT_USB)) {
                    usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
                            ? UsbPermission.Granted : UsbPermission.Denied;
                    connect();
                }
            }
        };
        mainLooper = new Handler(Looper.getMainLooper());
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        //WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置成全屏模式
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//强制为横屏
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.wheel_main);


        final Intent intent = getIntent();
        deviceId = intent.getIntExtra("device",0);
        portNum = intent.getIntExtra("port",0);
        baudRate = intent.getIntExtra("baud",9600);
        withIoManager = intent.getBooleanExtra("withIoManager",false);

        ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
        mConnectionState = (TextView) findViewById(R.id.connection_state);
        connect();
        initView();
    }
    private void initView() {
        mConnectionState = (TextView) findViewById(R.id.connection_state);
        send_count = (TextView) findViewById(R.id.send_count);
        wheelView_01 = (WheelView) findViewById(R.id.wheelView_01);
        textView_01 = (TextView) findViewById(R.id.textView_01);
        wheelView_02 = (WheelView) findViewById(R.id.wheelView_02);
        textView_02 = (TextView) findViewById(R.id.textView_02);
        sbValues = new StringBuffer();

        wheelView_01.setOnWheelViewMoveListener(
                new WheelView.OnWheelViewMoveListener() {
                    @Override
                    public void onValueChanged(int angle, int distance) {
                        textView_01.setText("角度：" + angle + "\n" +	"距离：" + distance);
                        if(angle<-115 && -180<angle){
                            angle = -(180+angle);
                        }
                        if (115<angle && angle<180){
                            angle = 180-angle;
                        }
                        if(!(angle>-65 && angle <65)){
                            angle = direction;
                        }
                        String dir = angle==0?"":(angle>0?"右":"左");
                        textView_01.setText("方向：" +dir+ Math.abs(angle) + "\n");

                        if( mConnected && direction!=angle)
                        {
                                send_count.setText("发送数据："+tx_count);
                                send("{\"dir\":"+String.valueOf(angle)+"}\r\n");
                        }else{
                            //Toast.makeText(UsbWheelActivity.this, "Deleted Successfully!", Toast.LENGTH_LONG).show();
                            //Toast toast = Toast.makeText(UsbWheelActivity.this, "设备没有连接！", Toast.LENGTH_SHORT);
                           // toast.show();
                        }
                        direction = angle;
                    }
                }, 100L);
        wheelView_02.setOnWheelViewMoveListener(
                new WheelView.OnWheelViewMoveListener() {
                    @Override
                    public void onValueChanged(int angle, int distance) {
                        //textView_02.setText("角度：" + angle + "\n" +	"距离：" + distance);
                        if(!(angle>-95 && angle <95)){
                            distance = -distance/10;
                        }
                        textView_02.setText("速度：" + distance+"%"+(distance==0?"":(distance>0?"前进":"倒车")));
                        if( mConnected && speed!=distance)
                        {
                                send_count.setText("发送数据："+tx_count);
                                send("{\"spe\":"+String.valueOf(distance)+"}\r\n");
                        }else{
                            //Toast.makeText(UsbWheelActivity.this, "Deleted Successfully!", Toast.LENGTH_LONG).show();
                            //Toast toast = Toast.makeText(UsbWheelActivity.this, "设备没有连接！", Toast.LENGTH_SHORT);
                            //toast.show();
                        }
                        speed = distance;
                    }
                }, 100L);
    }

    /*
     * Serial
     */
    @Override
    public void onNewData(byte[] data) {
        mainLooper.post(() -> {
            receive(data);
        });
    }

    @Override
    public void onRunError(Exception e) {
        mainLooper.post(() -> {
            status(R.string.connection_lost + e.getMessage());
            disconnect();
        });
    }

    @Override
    public void onResume() {
        super.onResume();
        registerReceiver(broadcastReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB));

        if(usbPermission == UsbPermission.Unknown || usbPermission == UsbPermission.Granted)
            mainLooper.post(this::connect);
    }

    @Override
    public void onPause() {
        if(connected) {
            status(getString(R.string.disconnected));
            disconnect();
        }
        unregisterReceiver(broadcastReceiver);
        super.onPause();
    }


    /*
     * Serial + UI
     */
    private void connect() {
        controlLines = new ControlLines( getWindow().getDecorView());
        UsbDevice device = null;
        UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        for(UsbDevice v : usbManager.getDeviceList().values())
            if(v.getDeviceId() == deviceId)
                device = v;
        if(device == null) {
            status(getString(R.string.connection_failed)+getString(R.string.device_not_found));
            return;
        }
        UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(device);
        if(driver == null) {
            driver = CustomProber.getCustomProber().probeDevice(device);
        }
        if(driver == null) {
            status(getString(R.string.connection_failed)+getString(R.string.no_driver_for_device));
            return;
        }
        if(driver.getPorts().size() < portNum) {
            status(getString(R.string.connection_failed)+getString(R.string.not_enough_ports_at_device));
            return;
        }
        usbSerialPort = driver.getPorts().get(portNum);
        UsbDeviceConnection usbConnection = usbManager.openDevice(driver.getDevice());
        if(usbConnection == null && usbPermission == UsbPermission.Unknown && !usbManager.hasPermission(driver.getDevice())) {
            usbPermission = UsbPermission.Requested;
            PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(INTENT_ACTION_GRANT_USB), 0);
            usbManager.requestPermission(driver.getDevice(), usbPermissionIntent);
            return;
        }
        if(usbConnection == null) {
            if (!usbManager.hasPermission(driver.getDevice()))
                status(getString(R.string.connection_failed)+getString(R.string.permission_denied));
            else
                status(getString(R.string.connection_failed)+getString(R.string.open_failed));
            return;
        }

        try {

            usbSerialPort.open(usbConnection);
            usbSerialPort.setParameters(baudRate, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
            if(withIoManager && 1==2) {
                usbIoManager = new SerialInputOutputManager(usbSerialPort, this);
                Executors.newSingleThreadExecutor().submit(usbIoManager);
            }
            status(getString(R.string.connected));
            mConnected = connected = true;
            controlLines.start();

        } catch (Exception e) {
            status(getString(R.string.connection_failed) + e.getMessage());
            disconnect();
        }
    }

    private void disconnect() {
        connected = false;
        controlLines.stop();
        if(usbIoManager != null)
            usbIoManager.stop();
        usbIoManager = null;
        try {
            usbSerialPort.close();
        } catch (IOException ignored) {}
        usbSerialPort = null;
    }

    private void send(String str) {
        status(R.string.send +" \n");///////测试
        if(!connected) {
            Toast.makeText(UsbWheelTestActivity.this, R.string.not_connected, Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            byte[] data = (str).getBytes();
            SpannableStringBuilder spn = new SpannableStringBuilder();

            receiveText = (TextView) findViewById(R.id.textView_03);
            receiveText.setText(str);
            usbSerialPort.write(data, WRITE_WAIT_MILLIS);
        } catch (Exception e) {
            onRunError(e);
        }
    }

    private void read() {
        if(!connected) {
            Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            byte[] buffer = new byte[8192];
            int len = usbSerialPort.read(buffer, READ_WAIT_MILLIS);
            receive(Arrays.copyOf(buffer, len));
        } catch (IOException e) {
            // when using read with timeout, USB bulkTransfer returns -1 on timeout _and_ errors
            // like connection loss, so there is typically no exception thrown here on error
            status(R.string.connection_lost + e.getMessage());
            disconnect();
        }
    }

    private void receive(byte[] data) {
        SpannableStringBuilder spn = new SpannableStringBuilder();
        spn.append(R.string.receive + data.length + R.string.bytes+" \n");
        if(data.length > 0)
            spn.append(HexDump.dumpHexString(data)+"\n");
        receiveText = (TextView) findViewById(R.id.textView_01);
        receiveText.append(spn);
    }

    public void status(String str) {
        SpannableStringBuilder spn = new SpannableStringBuilder(str+'\n');
        spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorStatusText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        receiveText = (TextView) findViewById(R.id.textView_01);
        receiveText.append(spn);
    }

    class ControlLines {
        private static final int refreshInterval = 200; // msec

        private Runnable runnable;
        private ToggleButton rtsBtn, ctsBtn, dtrBtn, dsrBtn, cdBtn, riBtn;

        ControlLines(View view) {
            runnable = this::run; // w/o explicit Runnable, a new lambda would be created on each postDelayed, which would not be found again by removeCallbacks

            rtsBtn = view.findViewById(R.id.controlLineRts);
            ctsBtn = view.findViewById(R.id.controlLineCts);
            dtrBtn = view.findViewById(R.id.controlLineDtr);
            dsrBtn = view.findViewById(R.id.controlLineDsr);
            cdBtn = view.findViewById(R.id.controlLineCd);
            riBtn = view.findViewById(R.id.controlLineRi);
            rtsBtn.setOnClickListener(this::toggle);
            dtrBtn.setOnClickListener(this::toggle);
        }

        private void toggle(View v) {
            ToggleButton btn = (ToggleButton) v;
            if (!connected) {
                btn.setChecked(!btn.isChecked());
                Toast.makeText(UsbWheelTestActivity.this, R.string.not_connected, Toast.LENGTH_SHORT).show();
                return;
            }
            String ctrl = "";
            try {
                if (btn.equals(rtsBtn)) { ctrl = "RTS"; usbSerialPort.setRTS(btn.isChecked()); }
                if (btn.equals(dtrBtn)) { ctrl = "DTR"; usbSerialPort.setDTR(btn.isChecked()); }
            } catch (IOException e) {
                status("set" + ctrl + "() failed: " + e.getMessage());
            }
        }

        private void run() {
            if (!connected)
                return;
            try {
                EnumSet<UsbSerialPort.ControlLine> controlLines = usbSerialPort.getControlLines();
                rtsBtn.setChecked(controlLines.contains(UsbSerialPort.ControlLine.RTS));
                ctsBtn.setChecked(controlLines.contains(UsbSerialPort.ControlLine.CTS));
                dtrBtn.setChecked(controlLines.contains(UsbSerialPort.ControlLine.DTR));
                dsrBtn.setChecked(controlLines.contains(UsbSerialPort.ControlLine.DSR));
                cdBtn.setChecked(controlLines.contains(UsbSerialPort.ControlLine.CD));
                riBtn.setChecked(controlLines.contains(UsbSerialPort.ControlLine.RI));
                mainLooper.postDelayed(runnable, refreshInterval);
            } catch (IOException e) {
                status("getControlLines() failed: " + e.getMessage() + " -> stopped control line refresh");
            }
        }

        void start() {
            if (!connected)
                return;
            try {
                EnumSet<UsbSerialPort.ControlLine> controlLines = usbSerialPort.getSupportedControlLines();
                if (!controlLines.contains(UsbSerialPort.ControlLine.RTS)) rtsBtn.setVisibility(View.INVISIBLE);
                if (!controlLines.contains(UsbSerialPort.ControlLine.CTS)) ctsBtn.setVisibility(View.INVISIBLE);
                if (!controlLines.contains(UsbSerialPort.ControlLine.DTR)) dtrBtn.setVisibility(View.INVISIBLE);
                if (!controlLines.contains(UsbSerialPort.ControlLine.DSR)) dsrBtn.setVisibility(View.INVISIBLE);
                if (!controlLines.contains(UsbSerialPort.ControlLine.CD))   cdBtn.setVisibility(View.INVISIBLE);
                if (!controlLines.contains(UsbSerialPort.ControlLine.RI))   riBtn.setVisibility(View.INVISIBLE);
                run();
            } catch (IOException e) {
                Toast.makeText(UsbWheelTestActivity.this, "getSupportedControlLines() failed: " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }

        void stop() {
            mainLooper.removeCallbacks(runnable);
            rtsBtn.setChecked(false);
            ctsBtn.setChecked(false);
            dtrBtn.setChecked(false);
            dsrBtn.setChecked(false);
            cdBtn.setChecked(false);
            riBtn.setChecked(false);
        }
    }
}
